package com.demo.sso.controller;

import com.demo.sso.common.config.Audience;
import com.demo.sso.common.constant.Const;
import com.demo.sso.common.constant.ShiroSessionRedisConstant;
import com.demo.sso.enity.LoginUser;
import com.demo.sso.service.LoginUserService;
import com.demo.sso.util.JwtHelper;
import com.demo.sso.util.MD5Utils;
import com.demo.sso.util.RedisUtil;
import com.demo.sso.util.ShiroSessionRedisUtil;
import io.jsonwebtoken.Claims;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.util.Date;

/**
 * 请添加模块注释
 *
 * 了解jwt可参考 https://www.jianshu.com/p/576dbf44b2ae
 *
 * jwt 实现     https://blog.csdn.net/wqh8522/article/details/78953379
 *
 * Swagger2 关于JSONObject参数在API文档中展示详细参数以及参数说明 https://blog.csdn.net/cy921107/article/details/82761575?utm_source=blogxgwz4
 *
 * @author fengjy
 * @date 2018-10-29 20:22
 */
@Controller
@Slf4j
@Api(value = "权限校验模块")
//@Api(value = "权限校验模块" , tags = "权限校验模块")
//@Api(tags={"权限校验模块"})
@RequestMapping("author")
public class AuthorController {
//    private Logger logger = Logger.getLogger(AuthorController.class);

    @Resource
    private LoginUserService loginUserService;

    @Resource
    private Audience audience;

    /**
     * 登录
     * @param user
     * @param request
     * @param response
     * @param model
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "sso登录", notes = "sso登录")
//    @ApiResponses(value = { @ApiResponse(code = 401, message = "请求未通过认证.", response = String.class) })
    @PostMapping(value = "login")
    public String login(LoginUser user, HttpServletRequest request, HttpServletResponse response, Model model) throws Exception {

        log.info("用户[{}]进入身份认证", user.getUserName());

        Subject subject = SecurityUtils.getSubject();
//        UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), MD5Utils.encrypt(user.getPassword()));
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword());

        String path = "login";

        try {
            token.setRememberMe(user.isRememberMe());
            subject.login(token);// 会跳到我们自定义的realm中

            log.info(user.getUserName() + "登陆了系统 " + new Date());

            LoginUser loginUser = loginUserService.chkByUserName(user.getUserName());

            log.info("登陆人用户信息 loginUser = [{}]", loginUser.toString());

            // 登陆成功后，将用户信息存放再session中，再更新redis
            String jwtToken = cacheLoginInfo(subject, loginUser, response);

            model.addAttribute("truename", loginUser.getTrueName());
            model.addAttribute("userName", loginUser.getUserName());
            model.addAttribute("phone", loginUser.getPhone());
            model.addAttribute("rolesId", loginUser.getRolesId());
            model.addAttribute("token", jwtToken);
            path = "index";

        } catch(UnknownAccountException e){
            e.printStackTrace();
            model.addAttribute("errorMessage","此用户名不存在！");
        }catch(IncorrectCredentialsException e){
            e.printStackTrace();
            model.addAttribute("errorMessage","密码不正确！");
        }catch (AuthenticationException e) {
            e.printStackTrace();
            model.addAttribute("errorMessage","用户身份认证失败！");
        }catch(Exception e){
            e.printStackTrace();
        }

        return path;
    }

    /**
     * 缓存登录人信息
     * @param subject
     * @param user
     * @param response
     */
    public String cacheLoginInfo (Subject subject, LoginUser user, HttpServletResponse response) throws Exception {
        //shiro管理的session
        Serializable sessionId = subject.getSession().getId();

        Session session = ShiroSessionRedisUtil.getSession(sessionId);
        if (session == null) {
            session = subject.getSession();
        }

        ///一些用户查找逻辑，将用户、权限等信息放到session中，再更新redis
        session.setAttribute(Const.SESSION_USER + user.getUserId(), user);
//        ShiroSessionRedisUtil.updateSession(session);

        //校验成功，生成一条token存到redis中，key为SSOTOKEN_USERID, 并以SSOTOKENID为key, 放到cookie中
        String token = JwtHelper.createJWT(user.getUserName()
                , user.getUserId()
                , user.getRolesId().toString()
                , audience.getClientId()
                , audience.getName()
                , audience.getExpiresSecond() * 1000
                , audience.getBase64Secret());

        RedisUtil.setString(ShiroSessionRedisConstant.SHIROSESSION_REDIS_DB
                ,ShiroSessionRedisConstant.SSOTOKEN_REDIS_PREFIX+user.getUserId()
                , token
                , ShiroSessionRedisConstant.SHIROSESSION_REDIS_EXTIRETIME);

        response.setHeader("Authorization", "Bearer " + token);
        return "Bearer " + token;
    }

    /**
     * 登出
     * @param request
     * @param response
     * @param model
     * @return
     * @throws Exception
     */
    @PostMapping(value = "loginOut")
    public String loginOut(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception {

        Subject subject = SecurityUtils.getSubject();

        try {
            //shiro管理的session
            Session session = subject.getSession();
//            Serializable sessionId = subject.getSession().getId();
//            Session session = ShiroSessionRedisUtil.getSession(sessionId);

            String token = request.getHeader("Authorization").substring(7, request.getHeader("Authorization").length());
            Claims claims = JwtHelper.parseJWT(token, audience.getBase64Secret());
            // 删除缓存中的用户信息
            session.removeAttribute(Const.SESSION_USER + claims.get("userid"));
//            ShiroSessionRedisUtil.deleteSession(session);

        }catch(Exception e){
            model.addAttribute("errorMessage","登出失败！");
            e.printStackTrace();
        }

        return "login";
    }

}
